package mymyoux.core
{
	import mymyoux.events.AMFPHPEvent;
	import mymyoux.events.HashEvent;
	import mymyoux.events.MEventDispatcher;
	import mymyoux.io.AMFPHP;
	import mymyoux.utils.Hash;
	/**
	 * Classe statique gérant la traduction de l'application de manière automatisée.
	 * @author Mymyoux
	 * @version 2.0
	 */
	public class Lang
	{
		/**
		 * Si <code>DEBUG=<b>true</b></code> alors les clés apparaissent le temps du chargement.<br>Si<code>DEBUG=<b>false</b></code> les champs de textes sont vides.
		 */
		static public const DEBUG:Boolean=true;
		/**
		 * Prefixe de l'évènement envoyée lorsqu'une traduction est modifiée.
		 */
		static protected const UPDATE:String="update";
		/**
		 * Permet de dispatcher un évènement lorsqu'une traduction a été mise à jour.
		 */
		static protected var dispatcher:MEventDispatcher;
		/**
		 * @private
		 */
		static protected var _idLanguage:uint=0;
		/**
		 * HashTable des traductions.
		 */
		static protected var hash:Hash;
		/**
		 * @private
		 */
		static protected var _method:String="getTraduction";
		/**
		 * @private
		 */
		 static protected var _languagesMethod:String="getLanguages";
		/**
		 * @private
		 */
		static protected var _class:String="Traduction";
		/**
		 * @private
		 */
		static protected var _languages:Array;
		/**
		 * Permet de temporiser les requêtes pour n'envoyer que les requêtes nécessaires lors de plusieurs demandes de traductions rapprochées.
		 */
		static protected var tampon:Tampon;
		/**
		 * Crée un objet de type Lang. Ne doit normalement jamais être appelée car l'ensemble de la classe est statique.
		 */
		public function Lang()
		{
			init();
		}
		/**
		 * Initialise la HashTable de la classe.
		 * @see mymyoux.utils.Hash
		 */
		static protected function init():void
		{
			if(hash==null)
			{
				tampon=new Tampon();
				dispatcher=new MEventDispatcher();
				hash=new Hash();
				hash.addEventListener(HashEvent.UPDATE,onUpdate);
				hash.addEventListener(HashEvent.REMOVE,onUpdate);
				var v:AMFPHP=new AMFPHP(_class);
				v.call(_languagesMethod,onLanguages);
			}
		}
		/**
		 * Appelée lorsque la liste des langages a été récupérées.
		 * @param event Contient la liste des langages.
		 */
		protected static function onLanguages(event:AMFPHPEvent):void
		{
			if(event.success)
			{
				_languages=event.value;
			}	
		}
		/** 
		 * Liste des languages existants indéxés.
		 */
		static public function get languages():Array
		{
			return _languages;
		}
		/**
		 * Fonction que <code>Lang</code> questionne pour récupérer de nouvelles traductions quand celles-ci manquent.
		 * @default "getTraduction"
		 */
		public static function get method():String
		{
			return _method;
		}
		public static function set method(value:String):void
		{
			_method=value;
		}
		/**
		 * Fonction que <code>Lang</code> questionne pour récupérer la liste des <b>Langages</b> existants et leurs ids.
		 * @default "getLanguages"
		 */
		 public static function get languagesMethod():String
		{
			return _languagesMethod;
		}
		public static function set languagesMethod(value:String):void
		{
			_languagesMethod=value;
		}
		 
		/**
		 * Classe dans laquelle <code>Lang</code> exécute la fonction pour récupérer de nouvelles traductions quand celles-ci manquent.
		 * @default "Traduction"
		 * @see #method
		 */
		public static function get traductionClass():String
		{
			return _class;
		}
		public static function set traductionClass(value:String):void
		{
			_class=value;
		}
		/**
		 * Id du langage.
		 */
		public static function get idLanguage():uint
		{
			return _idLanguage;
		}
		public static function set idLanguage(value:uint):void
		{
			if(_idLanguage!=value)
			{
				init();
				_idLanguage=value;
				hash.clear();
				//AMFPHP.cancel( )
				/*tampon.destroy();
				tampon=new Tampon();*/
			}
		}
		/**
		 * Appelée lorsque de nouvelles traductions sont disponibles.
		 * @param event contient les nouvelles traductions. Appel pour chacune d'elle <code>put</code>.
		 * @see #put()
		 */
		protected static function onNewTrad(event:AMFPHPEvent):void
		{
			//trace("event : "+event);
			if(event.success && event.value!=undefined && event.value["id_language"]!=undefined && event.value["clef_requested"]!=undefined)
			{
				if(event.value["id_language"]==_idLanguage)
				{
				//	trace("accepté");
				//	trace(event);
					for (var p:String in event.value)
					{
						if(p!="id_language" && p!="clef_requested")//réservés
						{
							put(p,event.value[p]);
						}
					}
					tampon.loaded(event.value["clef_requested"]);
				}
				else
				{
					tampon.ask(event.value["clef_requested"]);
					tampon.loaded(	event.value["clef_requested"]);
				}
			}
			else
			{
				tampon.ask(tampon.asklist[0]);
				tampon.loaded(tampon.asklist[0]);
			}
		}
		/**
		 * Enregistre une nouvelle traduction.
		 * @param name Clé de la traduction.
		 * @param traduction Nouvelle traduction.
		 */
		static public function put(name:String,traduction:String):void
		{
			init();
			hash.put(name,traduction);
		//	trace("put : "+name+"="+traduction);
		}
		/**
		 * Retourne la traduction correspondant à la clé fournie si elle existe.
		 * @param name Clé dont on souhaite récupérer la traduction.
		 * @return La traduction si elle existe sinon 
		 * un appel au serveur est effectué pour la récupérer (un appel à onNewTrad sera effectué) et la clé est retournée telle quelle.
		 * @see #onNewTrad()
		 * @see #ask()
		 */
		static public function get(name:String):String
		{
			init();
		//	trace("demande : "+name+" "+ hash.containsKey(name));
			if(hash.containsKey(name))
			{
				return hash.get(name) as String;
			}else
			{
				tampon.ask(name);
				return name;
			}
		}
		/**
		 * Renvoie un boolean indiquant si la traduction est présente ou non.
		 * @param name Clé dont on cherche à savoir si la traduction existe ou non.
		 * @return <code><b>true</b></code> Si la clé est associée à une traduction, <code><b>false</b></code> sinon.
		 */
		static public function has(name:String):Boolean
		{
			init();
			return hash.containsKey(name);
		}
		/**
		 * Demande la traduction du mot <code>name</code>.
		 * @param name Mot clé dont on demande la traduction.
		 */
		 static public function ask(name:String):void
		 {
		 	var v:AMFPHP=new AMFPHP(_class);
			v.call(method,onNewTrad,_idLanguage,name);	
		 }
		/**
		 * Supprime une clé et sa traduction de la HashTable.
		 * N'est normalement jamais utilisée.
		 * @param name Clé à supprimer.
		 */
		static public function rem(name:String):void
		{
			init();
			if(hash.containsKey(name))
			{
				hash.remove(name);
			}
		}
		/**
		 * Traduit un texte en y repérant les clés qui sont précédées d'un <code>$</code>
		 * @param texte Texte à traduire.
		 * @return Le Texte traduit.
		 */
		static public function trad(texte:String):String
		{
			init();
			var reg:RegExp=new RegExp("\\$\\(([^\\)]+)\\)","g");
			var tab:Array=new Array();
			var missing:Boolean=false;
			var temp:Array;
			while(temp=reg.exec(texte))
			{
				tab.push(temp[1]);
			}
			for(var p:String in tab)
			{
				reg=new RegExp("\\$\\("+tab[p]+"\\)","g");
				texte=texte.replace(reg,get(tab[p]));
				if(!hash.containsKey(tab[p]))
				{
					missing=true;
				}
			}
			return missing && !DEBUG?"":texte;
		}
		/**
		 * Ajoute un écouteur sur une clé.
		 * @param name Clé.
		 * @param func Listener.
		 */
		static public function watch(name:String,func:Function):void
		{
			init();
			dispatcher.addEventListener(UPDATE+name,func);
		}
		/**
		 * Retire un écouteur sur une clé.
		 * @param name Clé.
		 * @param func Listener.
		 */
		static public function unwatch(name:String,func:Function):void
		{
			init();
			dispatcher.removeEventListener(UPDATE+name,func);
		}
		/**
		 * Appelée lors de la modification d'une traduction.
		 * @param event Contient la nouvelle traduction <i>(clé + valeur)</i>.
		 */
		static protected function onUpdate(event:HashEvent):void
		{
		//	trace("dispatch :"+event);
			dispatcher.dispatchEvent(new HashEvent(UPDATE+event.key,event.key,event.value,event.bubbles,event.cancelable));
		}
	}
}
import mymyoux.core.Lang;
/**
 * Temporise les requêtes AMFPHP pour ne pas effectuer celles rendues obsolètes par les précédentes.
 */
internal class Tampon
{
	/**
	 * Liste des clés dont la traduction a été demandées.
	 */
	protected var list:Array;
	/**
	 * Crée un objet de type Tampon et initialise <code><b>list</b></code>.
	 */
	public function Tampon()
	{
		list=new Array();
	}
	public function get asklist():Array
	{
		return this.list;
	}
	/**
	 * Appelée par Lang pour rajouter une demande de traduction.
	 * Si <code><b>list.length=0</b></code> : La requête est effectuée immmédiatement.
	 * Si <code><b>list.length>0</b></code> : La requête est mise en attente sous réserve qu'elle soit encore utile.
	 * @param name Clé dont Lang demande la traduction.
	 * @see mymyoux.core.Lang
	 */
	public function ask(name:String):void
	{
	//	trace("ask "+name+" => "+list);
		if(list.length==0)
		{
			Lang.ask(name);
		}
		list.push(name);
	}
	/**
	 * Appelée par Lang lorsqu'une clé a reçu une traduction.
	 * @name Clé à l'origine de la requête traduction. Permet d'éviter un croisement de traduction lors d'un changement de langue.
	 */
	public function loaded(name:String):void
	{
//		trace(name+" vs "+list[0]+" => "+list);
		if(list[0]==name)
		{
			list.shift();
			while(list.length>0)
			{
		//		trace(list[0]);
				if(Lang.has(list[0]))
				{
					list.shift();
				}else
				{
					Lang.ask(list[0]);
					break;
				}
				//TODO:AMFPHP Cancel!
			}
		}
	}
	/**
	 * Supprime le tableau list
	 */
	public function destroy():void{
		list=new Array();
	}
}